package org.hackystat.sdsa.analyzer.control.compressor;

import java.util.ArrayList;
import java.util.List;

import org.hackystat.sdsa.analyzer.SdsaAnalyzerException;
import org.hackystat.sdsa.analyzer.model.activity.Activity;
import org.hackystat.sdsa.analyzer.model.activity.edit.DocumentEditActivity;
import org.hackystat.sdsa.analyzer.model.activity.edit.EditActivity;
import org.hackystat.sdsa.analyzer.model.activity.edit.ProductionEditActivity;
import org.hackystat.sdsa.analyzer.model.activity.edit.UnitTestEditActivity;
import org.hackystat.utilities.tstamp.Tstamp;

/**
 * Compresses the editing activities.
 * 
 * @author hongbing
 *
 */
public class EditCompressor implements Compressor {

  /**
   * Compresses editing development activities.
   * 
   * @param activityList List of development activities.
   * @throws SdsaAnalyzerException If problem occurs.
   */
  public void compress(List<Activity> activityList) throws SdsaAnalyzerException {
    List<CompressorUtil.BeginEndPair> editActivityPairs = 
      findContinuousEditActivities(activityList);
    
    List<Activity> activityListCopy = new ArrayList<Activity>();
    
    int beginEndPairIndex = 0;
    
    // Copy the activities to a temporary list.
    for (int index = 0; index < activityList.size(); index++) {
      Activity activity = activityList.get(index);
      
      if (beginEndPairIndex < editActivityPairs.size()) {
        CompressorUtil.BeginEndPair beginEnd = editActivityPairs.get(beginEndPairIndex);
        if (index == beginEnd.getBegin() ) {
          if (beginEnd.isMultiple()) {
            EditActivity newEditActivity = groupMultipleEditActivities(activityList, beginEnd);
            activityListCopy.add(newEditActivity);
            
            index += (beginEnd.length() - 1);
          }
          else {
            activityListCopy.add(activity);
          }
          
          // Increments the index of multiple edit activities.
          beginEndPairIndex++;
        }
        else {
          activityListCopy.add(activity);
        }
      }
      else {
        activityListCopy.add(activity);
      }
    }
    
    // Clear and refill the list with grouped edit activity.
    activityList.clear();
    activityList.addAll(activityListCopy);
  }

  /**
   * Groups continuous identical file edit actions together.  
   * 
   * @param activityList Activity list.
   * @return List of continuous identical file edit actions. 
   */
  private List<CompressorUtil.BeginEndPair> findContinuousEditActivities(
      List<Activity> activityList) {
    List<CompressorUtil.BeginEndPair> continuoutEditList = 
                              new ArrayList<CompressorUtil.BeginEndPair>();
   
    
    String latestEditFile = null;
    CompressorUtil.BeginEndPair latestPair = null;
    
    int index = 0;
    for (Activity activity : activityList) {
      // Check edit activity.
      if (activity instanceof EditActivity) {
        EditActivity editActivity = (EditActivity) activity;
        String editFile = editActivity.getResourceFile();
        if (latestEditFile == null) {
          // Simply start a new edit begin-end pair.
          latestPair = new CompressorUtil.BeginEndPair();
          continuoutEditList.add(latestPair);
          
          latestPair.setBegin(index);
          latestEditFile = editActivity.getResourceFile();
        }
        else if (editFile.equals(latestEditFile)) {
          latestPair.setEnd(index);
        }
        else { // Update the end of edit activities.
          // The edit file is changed from one to the the other
          // Close the last pair.
          latestPair.setEnd(index - 1);
          
          // Start a new edit begin-end pair.
          latestPair = new CompressorUtil.BeginEndPair();
          continuoutEditList.add(latestPair);

          latestPair.setBegin(index);
          latestEditFile = editActivity.getResourceFile();
        }
      }
      else { // Non-edit activity.
        if (latestEditFile != null) { 
          // Close the previous edit activity.
          latestPair.setEnd(index - 1);
          latestPair = null;
          latestEditFile = null;
        }  
      }
      
      // Increment the index by one.
      index++;
    }
    
    return continuoutEditList; 
  }
  
  /**
   * Makes new edit action from continuous edit action group. 
   * 
   * @param activityList Edit activity.
   * @param pair Begin end pair.
   * @return Newly created edit activity.
   * @throws SdsaAnalyzerException If problem occurs.
   */
  private EditActivity groupMultipleEditActivities(List<Activity> activityList, 
      CompressorUtil.BeginEndPair pair) throws SdsaAnalyzerException {
    if (pair == null || !pair.isMultiple()) {
      throw new SdsaAnalyzerException("No need to group a single edit activity"); 
    }
    
    // First edit activity
    EditActivity firstEdit = (EditActivity) activityList.get(pair.getBegin());
    // Last edit activity
    EditActivity lastEdit = (EditActivity) activityList.get(pair.getEnd());
    
    // Duration action
    long durationLong = Tstamp.diff(firstEdit.getTimestamp(), lastEdit.getTimestamp());
    int duration = Math.round(durationLong / 1000.0f);
    
    // Documentation edit action. 
    if (lastEdit instanceof DocumentEditActivity) {
      DocumentEditActivity docEdit = new DocumentEditActivity();
      docEdit.setTimeStamp(firstEdit.getTimestamp());
      docEdit.setResourceUri(firstEdit.getResourceUri());
      docEdit.setDuration(duration);
      
      docEdit.setFileSize(lastEdit.getFileSize());
      int additionalSize = lastEdit.getFileSize() - firstEdit.getFileSize(); 
      docEdit.setFileSizeIncrease(firstEdit.getFileSizeIncrease() + additionalSize);
 
      return docEdit;
    }
    else if (lastEdit instanceof UnitTestEditActivity) {
      UnitTestEditActivity last = (UnitTestEditActivity) lastEdit;
      UnitTestEditActivity unitTestEdit = new UnitTestEditActivity();

      unitTestEdit.setTimeStamp(firstEdit.getTimestamp());
      unitTestEdit.setResourceUri(firstEdit.getResourceUri());
      unitTestEdit.setDuration(duration);

      unitTestEdit.setFileSize(lastEdit.getFileSize());
      int additionalSize = last.getFileSize() - firstEdit.getFileSize(); 
      unitTestEdit.setFileSizeIncrease(firstEdit.getFileSizeIncrease() + additionalSize);

      // Number of method after edit
      unitTestEdit.setNumOfMethods(last.getNumOfMethods());
      // Number of test method after edit
      unitTestEdit.setNumOfTestMethods(last.getNumOfTestMethods());
      // Number of statement after edit
      unitTestEdit.setNumOfStatements(last.getNumOfStatements());
      // Number of test assertion after edit
      //newUnitTestEdit.setNumOfStatements(last.getNumOfTestAssertions());
      unitTestEdit.setNumOfTestAssertions(last.getNumOfTestAssertions());
      
      // newly created test object has no method and statement information
      if (firstEdit instanceof DocumentEditActivity) {
        unitTestEdit.setMethodIncrease(last.getNumOfMethods());
        // Test method increase in consecutive edit
        unitTestEdit.setTestMethodIncrease(last.getNumOfTestMethods());
        // Statement increase in consecutive edit
        unitTestEdit.setStatementIncrease(last.getNumOfStatements());
        // Test assertion
        unitTestEdit.setTestAssertionIncrease(last.getNumOfTestAssertions());        
        
      }
      // Test object has no any test method or statement at the very beginning.
      else if (firstEdit instanceof ProductionEditActivity) {
        ProductionEditActivity first = (ProductionEditActivity) firstEdit;
        // Method increase in consecutive edit
        int additionalMethods = last.getNumOfMethods() - first.getNumOfMethods(); 
        unitTestEdit.setMethodIncrease(first.getMethodIncrease() + additionalMethods);
        // Test method increase in consecutive edit
        unitTestEdit.setTestMethodIncrease(last.getNumOfTestMethods());
        
        // Statement increase in consecutive edit
        int additionalStatements = last.getNumOfStatements() - first.getNumOfStatements();
        unitTestEdit.setStatementIncrease(first.getStatementIncrease() + additionalStatements);
        // Test assertion
        unitTestEdit.setTestAssertionIncrease(last.getNumOfTestAssertions());        
      }
      else { // Continuous editing on test object.
        UnitTestEditActivity first = (UnitTestEditActivity) firstEdit;
        // Method increase in consecutive edit
        int additionalMethods = last.getNumOfMethods() - first.getNumOfMethods(); 
        unitTestEdit.setMethodIncrease(first.getMethodIncrease() + additionalMethods);

        // Test method increase in consecutive edit
        int addedTestMethods = last.getNumOfTestMethods() - first.getNumOfTestMethods(); 
        unitTestEdit.setTestMethodIncrease(first.getTestMethodIncrease() + addedTestMethods);
        
        // Statement increase in consecutive edit
        int additionalStatements = last.getNumOfStatements() - first.getNumOfStatements();
        unitTestEdit.setStatementIncrease(first.getStatementIncrease() + additionalStatements);

        // Assertion increase in consecutive edit
        int additionalTestAssertion = last.getNumOfTestAssertions() -
                                      first.getNumOfTestAssertions();
        unitTestEdit.setTestAssertionIncrease(first.getTestAssertionIncrease() +
                                       additionalTestAssertion);
      }
      
      return unitTestEdit;
    }
    else {
      ProductionEditActivity last = (ProductionEditActivity) lastEdit;
 
      ProductionEditActivity prodEdit = new ProductionEditActivity();
      prodEdit.setTimeStamp(firstEdit.getTimestamp());
      prodEdit.setResourceUri(firstEdit.getResourceUri());
      prodEdit.setDuration(duration);
      
      // File size
      prodEdit.setFileSize(lastEdit.getFileSize());
      int additionalSize = last.getFileSize() - firstEdit.getFileSize(); 
      prodEdit.setFileSizeIncrease(firstEdit.getFileSizeIncrease() + additionalSize);

      // Number of method after edit
      prodEdit.setNumOfMethods(last.getNumOfMethods());
      // Number of statement after edit
      prodEdit.setNumOfStatements(last.getNumOfStatements());

      // Continuous edit
      if (firstEdit instanceof ProductionEditActivity) {
        ProductionEditActivity first = (ProductionEditActivity) firstEdit;
        
        // Method increase in consecutive edit
        int additionalMethods = last.getNumOfMethods() - first.getNumOfMethods(); 
        prodEdit.setMethodIncrease(first.getMethodIncrease() + additionalMethods);

        // Statement increase in consecutive edit
        int additionalStatements = last.getNumOfStatements() - first.getNumOfStatements();
        prodEdit.setStatementIncrease(first.getStatementIncrease() + additionalStatements);
      }
      else { // Newly created object has no method and statement information
        prodEdit.setMethodIncrease(last.getNumOfMethods());
        prodEdit.setStatementIncrease(last.getNumOfStatements());
      }
      
      return prodEdit; 
    }
  }
}
